package com.manner.groupon.service.store.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.manner.groupon.common.BaseResponse;
import com.manner.groupon.common.PageResult;
import com.manner.groupon.common.ResultUtils;
import com.manner.groupon.constant.GrouponFlowTitle;
import com.manner.groupon.exception.CustomException;
import com.manner.groupon.mapper.*;
import com.manner.groupon.model.dto.search.StoreRequest;
import com.manner.groupon.model.dto.search.StoreSearch;
import com.manner.groupon.model.dto.shop.ChargeOffGrouponCouponRequest;
import com.manner.groupon.model.dto.store.StoreSaveDTO;
import com.manner.groupon.model.entity.*;
import com.manner.groupon.model.enums.OrderStatus;
import com.manner.groupon.model.enums.RoleEnums;
import com.manner.groupon.model.vo.*;
import com.manner.groupon.service.groupon.GrouponCouponsService;
import com.manner.groupon.service.store.StoreService;
import com.manner.groupon.utils.AddressUtil;
import com.manner.groupon.utils.GrouponOrderIdGenerate;
import com.manner.groupon.utils.IdUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.manner.groupon.constant.BusinessConstant.*;
import static com.manner.groupon.constant.CommonConstant.STORE_LIST;
import static com.manner.groupon.constant.RedisCacheKeyPrefix.STORE_VISIT_NUMBER_OF_PEOPLE;
import static com.manner.groupon.constant.SystemDictConstant.FIRST_ORDER_SCALE;
import static com.manner.groupon.model.enums.OrderStatus.*;

/**
 * @author manner
 * @description 针对表[tb_shop(商家表)]的数据库操作Service实现
 * @createDate 2024-10-08 22:54:40
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class StoreServiceImpl extends ServiceImpl<StoreMapper, Store> implements StoreService {

    private final StoreMapper storeMapper;
    private final GrouponCouponsService grouponCouponsService;
    private final GrouponCouponsMapper grouponCouponsMapper;
    private final GrouponOrderMapper grouponOrderMapper;
    private final StoreLinkManMapper storeLinkManMapper;
    private final StoreAccountFlowMapper storeAccountFlowMapper;
    private final SysDictMapper sysDictMapper;
    private final UserMapper userMapper;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * @param phone 登录后台的门店联系人手机账号
     * @return List<Store>
     * @description: 根据联系人手机号查询联系人信息, 并返回名下的所有店铺信息
     * @author Manner
     * @date 2024/12/8 23:49
     */
    public List<Store> getAllStoreByLinkmanPhone(String phone) {
        //查询联系人信息
        StoreLinkMan storeLinkMan = storeLinkManMapper.selectOne(new LambdaQueryWrapper<StoreLinkMan>().eq(StoreLinkMan::getPhone, phone));
        //联系人名下所有店铺信息
        List<Store> stores = storeMapper.selectList(new LambdaQueryWrapper<Store>().eq(Store::getAdminId, storeLinkMan.getId()));
        return stores;
    }

    /**
     * @param phone 登录后台的门店联系人手机账号
     * @return LinkmanLoadStoreListVo
     * @description: 根据手机号获取门店联系人名下的一个店铺信息
     * @author Manner
     * @date 2024/12/8 23:48
     */
    @Override
    public LinkmanLoadStoreListVo getLinkmanOneStore(String phone) {
        if (StrUtil.isBlank(phone)) {
            throw new CustomException("登录的门店联系人手机账号为空!");
        }
        //查询该联系人名下的所有店铺信息
        List<Store> stores = getAllStoreByLinkmanPhone(phone);
        //判断名下是否有门店存在
        if (CollUtil.isEmpty(stores)) {
            throw new CustomException("您名下还未注册店铺!");
        }
        LinkmanLoadStoreListVo linkmanLoadStoreListVo = BeanUtil.copyProperties(stores.get(0), LinkmanLoadStoreListVo.class);
        return linkmanLoadStoreListVo;
    }

    /**
     * @param phone 登录后台的门店联系人手机账号
     * @return List<LinkmanLoadStoreListVo>
     * @description: 根据手机号获取门店联系人名下的所有店铺信息
     * @author Manner
     * @date 2024/12/8 22:10
     */
    @Override
    public List<LinkmanLoadStoreListVo> getLinkmanAllStore(String phone) {
        if (StrUtil.isBlank(phone)) {
            throw new CustomException("登录的门店联系人手机账号为空!");
        }
        //查询该联系人名下的所有店铺信息
        List<Store> stores = getAllStoreByLinkmanPhone(phone);
        //判断名下是否有门店存在
        if (CollUtil.isEmpty(stores)) {
            throw new CustomException("您名下还未注册店铺!");
        }
        //准备过滤数据的容器
        List<LinkmanLoadStoreListVo> linkmanLoadStoreListVos = new ArrayList<>();
        stores.forEach(store -> {
            //遍历复制数据
            linkmanLoadStoreListVos.add(BeanUtil.copyProperties(store, LinkmanLoadStoreListVo.class));
        });
        return linkmanLoadStoreListVos;
    }


    /**
     * @param totalAmount
     * @param grouponOrderId
     * @param storeLinkMan
     * @description: 检查中介客户是否为平台第一笔订单, 进行给内部员工的二级分润,划账记录流水
     * @author Manner
     * @date 2024/11/22 18:51
     */
    @Transactional(rollbackFor = Throwable.class)
    public void oneOrderSecondaryProfitSharing(BigDecimal totalAmount, String grouponOrderId, StoreLinkMan storeLinkMan) {
        //首先查询该联系人名下的所有商户门店
        List<Store> stores = storeMapper.selectList(new LambdaQueryWrapper<Store>().eq(Store::getAdminId, storeLinkMan.getId()));
        //收集商户门店的编号
        List<String> storeIds = stores.stream().map(store -> store.getStoreNo()).collect(Collectors.toList());
        if (CollUtil.isEmpty(stores)) {
            throw new CustomException("该联系人暂未上线门店!");
        }
        //再查询当前这笔订单是否为该联系人名下所有店铺的首单
        GrouponOrder grouponOrder = grouponOrderMapper.selectOne(new LambdaQueryWrapper<GrouponOrder>().in(GrouponOrder::getStoreNo, storeIds).ne(GrouponOrder::getGrouponOrderId, grouponOrderId).eq(GrouponOrder::getStatus, HAVE_BEEN_USED.getCode()));
        //不是首单
        if (ObjectUtil.isNotEmpty(grouponOrder)) {
            throw new CustomException("当前订单已经不是该联系人的首单了!");
        }
        //查询这笔订单的商家分润流水记录,用于计算这笔订单二级分润的金额基数 (100 - 92 , 92 * 0.25)
        StoreAccountFlow storeAccountFlow = storeAccountFlowMapper.selectOne(new LambdaQueryWrapper<StoreAccountFlow>().eq(StoreAccountFlow::getGrouponOrderId, grouponOrderId));

        //首单,先把当前订单的可二级分余额算出来
        BigDecimal secondaryBalance = totalAmount.subtract(storeAccountFlow.getAmount());

        //查询首单返利的利率数
        String firstOrderProfit = sysDictMapper.selectOne(new LambdaQueryWrapper<SysDict>().eq(SysDict::getName, FIRST_ORDER_SCALE)).getValue();

        //整数类型转换成BigDecimal类型两位小数形式
        BigDecimal profitRate = new BigDecimal(Integer.valueOf(firstOrderProfit)).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
        //最终二级分润,内部员工所得
        BigDecimal staffMoney = secondaryBalance.multiply(profitRate);

        //更新内部员工的账面余额
        StoreLinkMan staffInfo = storeLinkManMapper.selectOne(new LambdaQueryWrapper<StoreLinkMan>().eq(StoreLinkMan::getLinkmanName, storeLinkMan.getIntroducerName()));
        staffInfo.setBookBalance(staffInfo.getBookBalance().add(staffMoney));
        //更新数据库
        storeLinkManMapper.updateById(staffInfo);
        //生成订单流水编号
        storeAccountFlow.setId(null);
        String orderFlowId = GrouponOrderIdGenerate.generateOrderFlowNumber();
        storeAccountFlow.setStoreFlowId(orderFlowId);
        storeAccountFlow.setTitle(GrouponFlowTitle.FIRST_ORDER_PROFIT_SHARING);
        storeAccountFlow.setAmount(staffMoney);
        storeAccountFlow.setUserId(staffInfo.getId());
        storeAccountFlow.setBalance(staffInfo.getBookBalance());
        //插入数据库记录流水
        storeAccountFlowMapper.insert(storeAccountFlow);
    }

    /**
     * 商家核销团购优惠券
     *
     * @param requestArgs 核销所需参数类
     * @return BaseResponse<Boolean>
     * @author Manner
     * @date 2024/10/13 17:01
     */
    @Override
    public BaseResponse<String> chargeOffGrouponCoupons(ChargeOffGrouponCouponRequest requestArgs) {
        //查询优惠券订单信息
        GrouponOrder grouponOrder = grouponOrderMapper.selectOne(new QueryWrapper<GrouponOrder>().eq("groupon_order_id", requestArgs.getGrouponOrderId()));
        //查询优惠券信息
        GrouponCoupons grouponCoupons = grouponCouponsMapper.selectById(grouponOrder.getGcId());
        //获取商家联系人数据
        StoreLinkMan storeLinkman = storeLinkManMapper.selectOne(new QueryWrapper<StoreLinkMan>().eq("phone", requestArgs.getPhone()));
        //获取商家数据
        List<Store> list = query().eq("admin_id", storeLinkman.getId()).list();
        //判断
        if (list.isEmpty()) {
            return ResultUtils.success("核销失败");
        }
        //转换
        List<String> collect = list.stream().map(Store::getStoreNo).collect(Collectors.toList());
        //判断这个团购券是否属于这个联系人的商家
        Long count = grouponCouponsMapper.selectCount(new QueryWrapper<GrouponCoupons>().in("store_id", collect).eq("id", grouponCoupons.getId()));
        //判断优惠券是否已经下架
        if (grouponCoupons == null) {
            throw new CustomException("团购券失效不可使用!");
        }
        //判断团购优惠券订单是否属于该门店(前台带过来的商铺门店编号 和 优惠券订单里面的商铺门店编号作对比)
        if (count <= 0) {
            throw new CustomException("此团购券不属于该商家");
        }
        //判断优惠券已经使用
        if (grouponOrder.getStatus() == HAVE_BEEN_USED.getCode()) {
            throw new CustomException("您已使用过这张团购优惠券了");
        }
        //现在时间减去开始时间,得到已经生效的天数
        Long between = ChronoUnit.DAYS.between(grouponCoupons.getCreateTime(), LocalDateTime.now());
        //判断团购优惠券是否已经过期
        if (between >= grouponCoupons.getEffectDay()) {
            //过期就更新数据库中的订单状态信息
            grouponOrder.setStatus(OrderStatus.HAVE_EXPIRED.getCode());
            grouponCouponsMapper.updateById(grouponCoupons);
            throw new CustomException("团购优惠券已经过期!");
        }
        //核销成功,更改优惠券订单状态为已使用
        grouponOrder.setStatus(HAVE_BEEN_USED.getCode());
        grouponOrderMapper.updateById(grouponOrder);

        //判断该联系人是否是被内部员工邀请入驻平台的
        if (cn.hutool.core.util.StrUtil.isNotBlank(storeLinkman.getIntroducerName())) {
            //检查是否为该联系人名下所有的商户门店的第一单(进行内部员工二级分润)
            oneOrderSecondaryProfitSharing(grouponOrder.getTotalAmount(), grouponOrder.getGrouponOrderId(), storeLinkman);
        }

        //返回数据
        return ResultUtils.success("核销成功");
    }

    @Override
    public BaseResponse<PageResult<StoreVo>> search(StoreSearch storeSearch) {
        //查询缓存
        Boolean hasKey = redisTemplate.hasKey(STORE_LIST);
        //判断
        if (!hasKey) {
            //查询数据库
            List<Store> list = list();
            //准备Map集合
            Map<Store, Point> map = new HashMap<>();
            list.forEach(item -> {
                map.put(item, new Point(Double.valueOf(item.getLongitude()), Double.valueOf(item.getLatitude())));
            });
            //写入
            redisTemplate.opsForGeo().add(STORE_LIST, map);
        }
        //准备容器
        List<StoreVo> list = new ArrayList<>();
        //查询半径
        Distance distance = new Distance(storeSearch.getRadius(), Metrics.KILOMETERS);
        Circle circle = new Circle(new Point(storeSearch.getLongitude(), storeSearch.getLatitude()), distance);
        //查询数据
        GeoResults<RedisGeoCommands.GeoLocation<Store>> results = redisTemplate.opsForGeo().radius(STORE_LIST, circle, RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().sortAscending());
        long total = 0;
        //遍历
        for (GeoResult<RedisGeoCommands.GeoLocation<Store>> item : results) {
            //转换
            Store store = JSONUtil.toBean(JSONUtil.toJsonStr(item.getContent().getName()), Store.class);
            //获取距离
            double distanceItem = item.getDistance().getValue() * 1000;
            //存入容器
            list.add(new StoreVo(store, distanceItem, null, null));
            //数量加1
            total++;
        }
        //分页
        list = list.stream().filter(item -> {
            return StrUtil.isBlank(storeSearch.getName()) ? true : item.getName().contains(storeSearch.getName());
        }).filter(item -> {
            return item.getStatus() == 0;
        }).filter(item -> {
            return storeSearch.getType() == 0 ? true : item.getTypeId().equals(storeSearch.getType());
        }).skip((storeSearch.getCurrent() - 1) * storeSearch.getPageSize()).limit(storeSearch.getPageSize()).collect(Collectors.toList());
        list.forEach(item -> {
            //获取商家团购券数据
            List<GrouponCoupons> coupons = grouponCouponsService.query().eq("store_id", item.getStoreNo()).eq("status", 0).list();
            //设置值
            item.setList(coupons);
        });
        return ResultUtils.success(new PageResult<>(list, total));
    }

    @Override
    public BaseResponse<StoreVo> queryByNo(String no) {
        //查询数据库
        Store store = query().eq("store_no", no).one();
        //记录增加店铺的访问次数
        store.setNumberOfVisitors(store.getNumberOfVisitors() + 1L);
        storeMapper.updateById(store);
        //获取团购券数据
        List<GrouponCoupons> list = grouponCouponsService.query().eq("store_id", no).eq("status", 0).list();
        //查询联系人信息
        StoreLinkMan linkMan = storeLinkManMapper.selectOne(new QueryWrapper<StoreLinkMan>().eq("id", store.getAdminId()));
        //返回数据
        return ResultUtils.success(new StoreVo(store, 0, list, linkMan.getPhone()));
    }

    @Override
    public BaseResponse<PageResult<AdminStoreVO>> query_search(StoreRequest storeRequest) {
        //设置分页参数
        Page<Store> page = new Page<>(storeRequest.getCurrent(), storeRequest.getPageSize());
        //设置查询条件
        QueryWrapper<Store> wrapper = new QueryWrapper<Store>().like("name", '%' + storeRequest.getName() + '%');
        if (storeRequest.getIs_delete() != 2) {
            wrapper = wrapper.eq("status", storeRequest.getIs_delete());
        }
        //查询数据
        Page<Store> selectPage = storeMapper.selectPage(page, wrapper);
        //准备容器
        List<AdminStoreVO> container = new ArrayList<>();
        //获取联系人信息
        for (Store record : selectPage.getRecords()) {
            StoreLinkMan linkMan = storeLinkManMapper.selectById(record.getAdminId());
            AdminStoreVO adminStoreVo = BeanUtil.copyProperties(record, AdminStoreVO.class);
            adminStoreVo.setLinkman(linkMan.getLinkmanName());
            adminStoreVo.setPhone(linkMan.getPhone());
            adminStoreVo.setAccount(linkMan.getRemitAccount());
            adminStoreVo.setBookBalance(linkMan.getBookBalance());
            //设置数据
            container.add(adminStoreVo);
        }
        //返回数据
        return ResultUtils.success(new PageResult<>(container, selectPage.getTotal()));
    }

    @Override
    public BaseResponse<String> insert(StoreSaveDTO storeSaveDTO) {
        //先查询是否有这个联系人
        StoreLinkMan linkMan = storeLinkManMapper.selectOne(new QueryWrapper<StoreLinkMan>().eq("phone", storeSaveDTO.getPhone()));
        //判断
        if (ObjUtil.isNull(linkMan)) {
            linkMan = new StoreLinkMan(null, storeSaveDTO.getName(), storeSaveDTO.getPhone(), storeSaveDTO.getPhone().substring(5, storeSaveDTO.getPhone().length()), storeSaveDTO.getAccount(), storeSaveDTO.getBank(), storeSaveDTO.getPayee(), null, "店长", null, RoleEnums.STORE_ROLE.getId(), storeSaveDTO.getScale(), null, 0, null, null);
            //创建一个联系人
            storeLinkManMapper.insert(linkMan);
        }
        AddressUtil.Location address = new AddressUtil.Location();
        //获取商铺经纬度
        try {
            address = AddressUtil.getAddress(storeSaveDTO.getAddress());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //创建商铺数据
        save(new Store(null, IdUtils.generateId(), linkMan.getId(), storeSaveDTO.getName(), storeSaveDTO.getHours(), storeSaveDTO.getImg(), storeSaveDTO.getAddress(), storeSaveDTO.getTypeId(), address.getResult().getLocation().getLat(), address.getResult().getLocation().getLng(), storeSaveDTO.getLabel(), storeSaveDTO.getScale(), 0, storeSaveDTO.getNumberOfVisitors(), 0, null, null));
        //删除缓存
        redisTemplate.delete(STORE_LIST);
        //返回数据
        return ResultUtils.success("添加成功");
    }

    @Override
    public BaseResponse<List<Store>> query_phone(String phone) {
        //获取用户信息
        StoreLinkMan linkMan = storeLinkManMapper.selectOne(new QueryWrapper<StoreLinkMan>().eq("phone", phone));
        //查询门店数据
        List<Store> list = query().eq("admin_id", linkMan.getId()).list();
        //返回数据
        return ResultUtils.success(list);
    }

    @Override
    public BaseResponse<AdminStoreVO> query_id(Long id) {
        //获取门店数据
        Store store = getById(id);
        //获取商家数据
        StoreLinkMan storeLinkMan = storeLinkManMapper.selectById(store.getAdminId());

        AdminStoreVO adminStoreVo = BeanUtil.copyProperties(store, AdminStoreVO.class);
        adminStoreVo.setLinkman(storeLinkMan.getLinkmanName());
        adminStoreVo.setPhone(storeLinkMan.getPhone());
        adminStoreVo.setAccount(storeLinkMan.getRemitAccount());
        adminStoreVo.setBookBalance(storeLinkMan.getBookBalance());
        adminStoreVo.setPayee(storeLinkMan.getPayeeName());
        //返回数据
        return ResultUtils.success(adminStoreVo);
    }

    @Override
    public BaseResponse<String> updateStatus(Long id, Integer status) {
        //修改信息
        update().set("status", status).eq("id", id).update();
        //删除缓存
        redisTemplate.delete(STORE_LIST);
        //返回数据
        return ResultUtils.success("修改成功");
    }

    /**
     * @return BigDecimal
     * @description: 两个Long类型的数字相除再乘以100
     * @author Manner
     * @date 2024/12/9 19:05
     */
    public DataPanelVO calculateTheDifference(Long toDayCount, Long divisor, Long dividedNumber) {
        //防止被除数为零,出现by zero 异常
        if (BeanUtil.isEmpty(dividedNumber) || dividedNumber == 0) {
            return new DataPanelVO(toDayCount.toString(), BigDecimal.ZERO);
        }
        //相除的结果 * 100 获得总数对比的正负百分比数
        BigDecimal compareResult = BigDecimal.valueOf(divisor).divide(BigDecimal.valueOf(dividedNumber), 2, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(HUNDRED_NUMBER));
        return new DataPanelVO(toDayCount.toString(), compareResult);
    }

    /**
     * @param storeNo 店铺编号
     * @return BaseResponse<StoreHomeDataAssayVO>
     * @description: 商家后台主页数据展示分析
     * @author Manner
     * @date 2024/12/9 0:52
     */
    @Override
    public StoreHomeDataAssayVO getStoreHomeDataAssay(String storeNo) {
        if (StrUtil.isBlank(storeNo)) {
            throw new CustomException("门店编号信息异常!");
        }
        //准备返回数据的容器对象
        StoreHomeDataAssayVO storeHomeDataAssayVO = new StoreHomeDataAssayVO();
        //查询门店信息
        Store store = storeMapper.selectOne(new LambdaQueryWrapper<Store>().eq(Store::getStoreNo, storeNo));
        //设置门店分润利率
        storeHomeDataAssayVO.setProfit(store.getScale());
        //准备今天的起始结束时间
        LocalDate date = LocalDate.now();
        LocalDateTime startTime = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
        //查询这个店铺今天内的订单信息
        List<GrouponOrder> toDayGrouponOrders = grouponOrderMapper.selectList(new LambdaQueryWrapper<GrouponOrder>().eq(GrouponOrder::getStoreNo, storeNo).between(GrouponOrder::getCreateTime, startTime, endTime));
        //没有订单就设置为0
        if (CollUtil.isEmpty(toDayGrouponOrders)) {
            storeHomeDataAssayVO.setCount(0);
        }
        //设置门店当日订单总数
        storeHomeDataAssayVO.setCount(toDayGrouponOrders.size());
        //当日总收益
        BigDecimal toDayTotalProfit = BigDecimal.ZERO;
        //循环计算
        toDayGrouponOrders.forEach(grouponOrder -> {
            //包含就纳入计算
            if (getRevenueOrderStatus().contains(grouponOrder.getStatus())) {
                toDayTotalProfit.add(grouponOrder.getTotalAmount());
            }
        });
        //设置今日总收入
        storeHomeDataAssayVO.setPrice(toDayTotalProfit);

        //准备数据看板的容器
        List<DataPanelVO> dataPanelVOS = new ArrayList<>();
        //今日的总用户数
        Long toDayUserCount = userMapper.selectCount(null);
        //昨日的总用户数
        Long yesterdayUserCount = userMapper.selectCount(new LambdaQueryWrapper<User>().lt(User::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN)));
        //计算今天和昨天的用户数量差额数
        Long userDifferenceNumber = toDayUserCount - yesterdayUserCount;
        //存储数据看板---->曝光度数据
        dataPanelVOS.add(calculateTheDifference(toDayUserCount, userDifferenceNumber, yesterdayUserCount));

        //截止到今天目前的店铺访问次数
        Long toDayStoreVisitNumber = store.getNumberOfVisitors();
        //获取截止到昨日的该门店访问数量
        Long yesterdayStoreVisitNumber = Long.valueOf(stringRedisTemplate.opsForHash().get(STORE_VISIT_NUMBER_OF_PEOPLE + date.minusDays(ONE_NUMBER), storeNo).toString());
        //访问次数的对比结果数
        Long storeVisitDifferenceNumber = toDayStoreVisitNumber - yesterdayStoreVisitNumber;
        //存储数据面板--->访问人数
        dataPanelVOS.add(calculateTheDifference(toDayStoreVisitNumber, storeVisitDifferenceNumber, yesterdayStoreVisitNumber));

        //今日团购套餐售出的数量
        Long doDayOrderGrouponCouponNumber = Long.valueOf(toDayGrouponOrders.stream().map(GrouponOrder::getTicketNumber).reduce(0, Integer::sum));
        //昨天的所有订单数据
        List<GrouponOrder> yesterdayGrouponOrders = grouponOrderMapper.selectList(new LambdaQueryWrapper<GrouponOrder>().eq(GrouponOrder::getStoreNo, storeNo).between(GrouponOrder::getCreateTime, startTime.minusDays(ONE_NUMBER), endTime.minusDays(ONE_NUMBER)));
        //昨天团购套餐售出的数量
        Long yesterdayOrderGrouponCouponNumber = Long.valueOf(yesterdayGrouponOrders.stream().map(GrouponOrder::getTicketNumber).reduce(0, Integer::sum));

        //今天对比昨天团购券售卖的数量
        Long grouponCouponDifferenceNumber = doDayOrderGrouponCouponNumber - yesterdayOrderGrouponCouponNumber;
        //团购券售出差额数
        dataPanelVOS.add(calculateTheDifference(doDayOrderGrouponCouponNumber, grouponCouponDifferenceNumber, yesterdayOrderGrouponCouponNumber));

        //今日核销金额
        BigDecimal toDayWriteOffMoney = toDayGrouponOrders.stream()
                //过滤出状态为已核销的订单信息
                .filter(grouponOrder -> HAVE_BEEN_USED.getCode() == grouponOrder.getStatus()).map(GrouponOrder::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //昨日核销金额
        BigDecimal yesterdayWriteOffMoney = yesterdayGrouponOrders.stream()
                //过滤出状态为已核销的订单信息
                .filter(grouponOrder -> HAVE_BEEN_USED.getCode() == grouponOrder.getStatus()).map(GrouponOrder::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //今天对比昨天核销金额数
        BigDecimal writeOffDifferenceNumber = toDayWriteOffMoney.subtract(yesterdayWriteOffMoney);
        //防止被除数为零,出现by zero 异常
        if (BeanUtil.isEmpty(yesterdayWriteOffMoney) || yesterdayWriteOffMoney == BigDecimal.ZERO) {
            dataPanelVOS.add(new DataPanelVO(toDayWriteOffMoney.toString(), BigDecimal.ZERO));
        } else {
            //差额数除以昨天的金额数,再乘以100
            BigDecimal compareResult = writeOffDifferenceNumber.divide(yesterdayWriteOffMoney, 2, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100));
            dataPanelVOS.add(new DataPanelVO(toDayWriteOffMoney.toString(), compareResult));
        }
        //设置商家后台数据看板展示值
        storeHomeDataAssayVO.setDataPanels(dataPanelVOS);
        return storeHomeDataAssayVO;
    }

    /**
     * @param storeNo       店铺编号
     * @param startTime     开始时间
     * @param endTime       结束时间
     * @param successStatus 状态码集合
     * @return BigDecimal
     * @description: 根据时间区间和指定订单状态搜索订单信息集合
     * @author Manner
     * @date 2024/12/10 21:36
     */
    public BigDecimal byDateCalculationStoreIncome(String storeNo,
                                                   LocalDateTime startTime,
                                                   LocalDateTime endTime,
                                                   Set<Integer> successStatus) {
        //根据时间区间和指定状态集合查询团购券订单集合
        List<GrouponOrder> grouponOrders = grouponOrderMapper.selectList(new LambdaQueryWrapper<GrouponOrder>()
                .eq(GrouponOrder::getStoreNo, storeNo)
                .in(GrouponOrder::getStatus, successStatus)
                .between(GrouponOrder::getCreateTime, startTime, endTime)
        );
        //判断不为空
        if (CollUtil.isNotEmpty(grouponOrders)) {
            //lambda表达式循环叠加
            BigDecimal income = grouponOrders.stream()
                    .map(GrouponOrder::getTotalAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            return income;
        }
        return BigDecimal.ZERO;
    }

    /**
     * @param storeNo 店铺编号
     * @return StoreBusinessDataSummaryAssayVO
     * @description: 店铺门店数据汇总分析
     * @author Manner
     * @date 2024/12/10 17:46
     */
    @Override
    public StoreIncomeDataSummaryAssayVO loadStoreBusinessDataSummaryAssay(String storeNo) {
        if (StrUtil.isBlank(storeNo)) {
            throw new CustomException("店铺编号无效值!");
        }
        //准备返回数据的类型对象
        StoreIncomeDataSummaryAssayVO storeData = new StoreIncomeDataSummaryAssayVO();
        //获取当前年月日时间
        LocalDate currentDate = LocalDate.now();
        //今日开始时间
        LocalDateTime toDayStartTime = LocalDateTime.of(currentDate, LocalTime.MIN);
        //今日结束时间
        LocalDateTime toDayEndTime = LocalDateTime.of(currentDate, LocalTime.MAX);
        //设置今日的收益额
        storeData.setDailyIncomeAmount(byDateCalculationStoreIncome(storeNo, toDayStartTime, toDayEndTime, getRevenueOrderStatus()));

        //本周开始时间
        LocalDateTime startOfWeek = LocalDateTime.of(currentDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)), LocalTime.MIN);
        //本周结束时间
        LocalDateTime endOfWeek = LocalDateTime.of(currentDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)), LocalTime.MAX).plusDays(1).minusNanos(1);
        //设置周收益额
        storeData.setWeeklyIncomeAmount(byDateCalculationStoreIncome(storeNo, startOfWeek, endOfWeek, getRevenueOrderStatus()));

        //本月开始时间
        LocalDateTime startOfMonth = LocalDateTime.of(currentDate.with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
        //本月结束时间
        LocalDateTime endOfMonth = LocalDateTime.of(currentDate.with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX).plusDays(1).minusNanos(1);
        //设置月收益额
        storeData.setMonthlyIncomeAmount(byDateCalculationStoreIncome(storeNo, startOfMonth, endOfMonth, getRevenueOrderStatus()));
        return storeData;
    }

    /**
     * @param storeNo 店铺编号
     * @return BaseResponse<HashMap < List < Long>>>
     * @description: 获取店铺这个星期每天的订单数, 收益额, 退款额数据
     * @author Manner
     * @date 2024/12/10 22:31
     */
    @Override
    public HashMap<String, List<Long>> loadWeekEveryDayInformation(String storeNo) {
        //当前时间
        LocalDate today = LocalDate.now();
        //当前周的星期一时间
        LocalDateTime currentWeekMonDayDateTime = LocalDateTime.of(today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)), LocalTime.MIN);
        // 一周结束时间（即下周一）
        LocalDateTime weekEndDateTime = currentWeekMonDayDateTime.plusDays(7).minusSeconds(1);
        // 查询本周内所有订单数据
        List<GrouponOrder> ordersThisWeek = grouponOrderMapper.selectList(new LambdaQueryWrapper<GrouponOrder>().eq(GrouponOrder::getStoreNo, storeNo).between(GrouponOrder::getCreateTime, currentWeekMonDayDateTime, weekEndDateTime));
        // 根据创建日期对订单列表进行分组
        Map<LocalDate, List<GrouponOrder>> byDateGroupOrders = ordersThisWeek.stream().collect(Collectors.groupingBy(grouponOrder -> LocalDateTime.parse(grouponOrder.getCreateTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME).toLocalDate()));
        // 创建一个包含本周所有日期的列表
        List<LocalDate> weekDays = IntStream.rangeClosed(0, 6).mapToObj(dayDateTime -> currentWeekMonDayDateTime.plusDays(dayDateTime).toLocalDate()).collect(Collectors.toList());
        //准备返回数据的Map容器
        HashMap<String, List<Long>> storeWeekInformation = new HashMap<>();
        //收益额
        storeWeekInformation.put(REVENUE_AMOUNT, new ArrayList<>());
        //退款额
        storeWeekInformation.put(REFUND_AMOUNT, new ArrayList<>());
        //订单数
        storeWeekInformation.put(ORDER_COUNT, new ArrayList<>());
        weekDays.forEach(weekDay -> {
            List<GrouponOrder> grouponOrders = byDateGroupOrders.get(weekDay);
            //判断这周中的某一天这家店铺没有订单数据
            if (CollUtil.isEmpty(grouponOrders)) {
                //直接设置默认值
                storeWeekInformation.forEach((key, weekDataList) -> {
                    weekDataList.add(Long.valueOf(ZERO_NUMBER));
                });
            } else {
                //计算订单数
                Integer currentDayOrderCount = grouponOrders.size();
                //计算收益额
                BigDecimal currentDayRevenueAmount = grouponOrders.stream().filter(grouponOrder -> getRevenueOrderStatus().contains(grouponOrder.getStatus())).map(grouponOrder -> grouponOrder.getTotalAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
                //计算退款额
                BigDecimal currentDayRefundAmount = grouponOrders.stream().filter(grouponOrder -> getRefundOrderStatus().contains(grouponOrder.getStatus())).map(grouponOrder -> grouponOrder.getTotalAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
                //设置计算出来的数据
                storeWeekInformation.get(ORDER_COUNT).add(Long.valueOf(currentDayOrderCount));
                storeWeekInformation.get(REVENUE_AMOUNT).add(currentDayRevenueAmount.longValue());
                storeWeekInformation.get(REFUND_AMOUNT).add(currentDayRefundAmount.longValue());
            }
        });
        return storeWeekInformation;
    }

    /**
     * @param storeNo   店铺编号
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return List<GrouponOrder>
     * @description: 根据时间区间查询团购订单数据
     * @author Manner
     * @date 2024/12/11 4:40
     */
    public List<GrouponOrder> byDateTimeRangeGetGrouponOrderData(String storeNo,
                                                                 LocalDateTime startDate,
                                                                 LocalDateTime endDate) {
        List<GrouponOrder> grouponOrders = grouponOrderMapper.selectList(new LambdaQueryWrapper<GrouponOrder>()
                .eq(GrouponOrder::getStoreNo, storeNo)
                .between(GrouponOrder::getCreateTime, startDate, endDate)
        );
        return grouponOrders;
    }

    /**
     * @param dateTime     当前时间
     * @param weeklyRanges 一个月中的每周开始和结束时间集合
     * @return Integer
     * @description: 根据当天时间查询现在处于一个月中的第几周
     * @author Manner
     * @date 2024/12/11 8:35
     */
    public Integer findWeekNumber(LocalDateTime dateTime, List<LocalDateTime[]> weeklyRanges) {
        for (int i = 0; i < weeklyRanges.size(); i++) {
            LocalDateTime[] weekRange = weeklyRanges.get(i);
            if (!dateTime.isBefore(weekRange[0]) && !dateTime.isAfter(weekRange[1])) {
                return i + 1; // 周期编号从1开始
            }
        }
        // 如果没有找到合适的周期，则可能是28号之后的时间段
        return weeklyRanges.size() + ONE_NUMBER;
    }

    /**
     * @param weeklyRanges    一个月中的每周开始和结束时间集合
     * @param compareDataList 需要对比获得百分比结果的数据集合
     * @return List<DataPanelVO>
     * @description: 这周数据对比上周数据的整数比例
     * @author Manner
     * @date 2024/12/11 8:36
     */
    public List<DataPanelVO> lastWeekCompareNextWeekDataPercentageResult(List<LocalDateTime[]> weeklyRanges,
                                                                         List<String> compareDataList) {
        //创建数据看板集合
        List<DataPanelVO> dataPanelVOList = new ArrayList<>();
        //获取当前时间
        LocalDateTime currentDateTime = LocalDateTime.now();
        //当前时间处于本月第几周
        Integer weekNumber = findWeekNumber(currentDateTime, weeklyRanges);
        //数据环比
        for (int startNumber = ZERO_NUMBER; startNumber < compareDataList.size(); startNumber++) {
            //初始化对比结果
            BigDecimal compareResult = new BigDecimal(ZERO_NUMBER);
            if (startNumber >= weekNumber) {//未来时间不用计算
                log.info("本月未来周数据无需环比,开始时间: {},结束时间: {}", weeklyRanges.get(startNumber)[0], weeklyRanges.get(startNumber)[1]);
                dataPanelVOList.add(new DataPanelVO(ZERO_NUMBER.toString(), BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)));
                continue;
            }
            //第一周无需环比
            if (startNumber == ZERO_NUMBER) {
                log.info("第一周数据无需环比,开始时间: {},结束时间: {}", weeklyRanges.get(startNumber)[0], weeklyRanges.get(startNumber)[1]);
            } else {//不是第一周
                //上一周数据
                BigDecimal lastWeekData = new BigDecimal(compareDataList.get(startNumber - 1));
                //这一周数据
                BigDecimal thisWeekData = new BigDecimal(compareDataList.get(startNumber));
                //判断上周数据不为0,防止出现 by zero异常
                if (lastWeekData.intValue() > ZERO_NUMBER) {
                    //差额
                    compareResult = thisWeekData.subtract(lastWeekData).divide(lastWeekData, 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(HUNDRED_NUMBER));
                }

                if (lastWeekData.intValue() == ZERO_NUMBER) {
                    //上周为零就直接乘以100
                    compareResult = BigDecimal.ZERO;
                }
                if (thisWeekData.intValue() == ZERO_NUMBER) {
                    compareResult = BigDecimal.ZERO;
                }

            }
            dataPanelVOList.add(new DataPanelVO(compareDataList.get(startNumber), compareResult.intValue() == ZERO_NUMBER ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : compareResult));
        }
        return dataPanelVOList;
    }

    /**
     * @param storeNo 店铺编号
     * @return StoreWeekDataAssayVO
     * @description: 获取指定店铺在当前这个月中每个星期的订单数, 收益额, 退款额数据 ,以及每周的环比数据
     * @author Manner
     * @date 2024/12/11 4:15
     */
    @Override
    public StoreWeekDataAssayVO loadMonthEveryWeekInformation(String storeNo) {
        //准备返回的数据容器对象
        StoreWeekDataAssayVO storeWeekDataAssayVO = new StoreWeekDataAssayVO();
        //当前时间
        LocalDate todayDate = LocalDate.now();
        // 获取当月的第一天
        LocalDateTime firstDayOfMonth = LocalDateTime.of(todayDate.with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
        // 创建一个列表来保存每周的时间段
        List<LocalDateTime[]> weeklyRanges = new ArrayList<>();
        // 添加前四个星期的时间段
        for (int i = 0; i < 4; i++) {
            LocalDateTime weekStart = firstDayOfMonth.plusDays(i * 7);
            LocalDateTime weekEnd = weekStart.plusDays(7).minusSeconds(ONE_NUMBER);
            weeklyRanges.add(new LocalDateTime[]{weekStart, weekEnd});
        }
        //创建数据集合
        List<String> weekRevenues = new ArrayList<>();
        List<String> weekRefunds = new ArrayList<>();
        List<String> weekOrderCounts = new ArrayList<>();
        //循环查询
        weeklyRanges.forEach(weeklyRange -> {
            //每周的团购券订单数据
            List<GrouponOrder> grouponOrders = byDateTimeRangeGetGrouponOrderData(
                    storeNo,
                    weeklyRange[ZERO_NUMBER],
                    weeklyRange[ONE_NUMBER]
            );
            //周收益额
            BigDecimal weekRevenue = BigDecimal.ZERO;
            //周退款额
            BigDecimal weekRefund = BigDecimal.ZERO;
            //周订单数量
            Integer weekOrderCount = 0;
            //本月未来周没有数据
            if (CollUtil.isEmpty(grouponOrders)) {
                //打印日志即可
                log.info("当月未来周数据为空,开始时间: {},结束时间: {}", weeklyRange[ZERO_NUMBER], weeklyRange[ONE_NUMBER]);
            } else {
                //计算订单数
                weekOrderCount = grouponOrders.size();
                //计算收益额
                weekRevenue = grouponOrders.stream()
                        .filter(grouponOrder -> getRevenueOrderStatus().contains(grouponOrder.getStatus()))
                        .map(grouponOrder -> grouponOrder.getTotalAmount())
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                //计算退款额
                weekRefund = grouponOrders.stream()
                        .filter(grouponOrder -> getRefundOrderStatus().contains(grouponOrder.getStatus()))
                        .map(grouponOrder -> grouponOrder.getTotalAmount())
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            //添加数据
            weekRevenues.add(String.valueOf(weekRevenue.intValue()));
            weekRefunds.add(String.valueOf(weekRefund.intValue()));
            weekOrderCounts.add(weekOrderCount.toString());
        });
        //设置数据集合
        storeWeekDataAssayVO.setRevenueAmount(weekRevenues);
        storeWeekDataAssayVO.setRefundAmount(weekRefunds);
        storeWeekDataAssayVO.setOrderCount(weekOrderCounts);

        //准备数据环比容器
        HashMap<String, List<DataPanelVO>> weekDataCompareResult = new HashMap<>();
        //设置各项环比数据
        weekDataCompareResult.put("revenueCompareResult", lastWeekCompareNextWeekDataPercentageResult(weeklyRanges, weekRevenues));
        weekDataCompareResult.put("refundCompareResult", lastWeekCompareNextWeekDataPercentageResult(weeklyRanges, weekRefunds));
        weekDataCompareResult.put("orderCountCompareResult", lastWeekCompareNextWeekDataPercentageResult(weeklyRanges, weekOrderCounts));

        //设置环比数据
        storeWeekDataAssayVO.setDataCompareResult(weekDataCompareResult);
        return storeWeekDataAssayVO;
    }

    /**
     * @param storeNo 店铺编号
     * @return HashMap<List < Long>>
     * @description: 获取指定店铺在当前这个季度中每个月的订单数, 收益额, 退款额数据 ,以及每周的环比数据
     * @author Manner
     * @date 2024/12/11 8:41
     */
    @Override
    public HashMap<String, List<String>> loadQuarterEveryMonthInformation(String storeNo) {
        //创建返回数据的容器Map
        HashMap<String, List<String>> quarterEveryMonthData = new HashMap<>();
        quarterEveryMonthData.put("revenues", new ArrayList<>());
        quarterEveryMonthData.put("refunds", new ArrayList<>());
        quarterEveryMonthData.put("orderCounts", new ArrayList<>());

        //当前时间
        LocalDate currentDate = LocalDate.now();
        //当前所处季度数字
        Integer currentQuarterNumber = (currentDate.getMonthValue() - 1) / 3 + 1;
        //季度开始时间
        LocalDate startOfQuarter = currentDate.withMonth((currentQuarterNumber - 1) * 3 + 1).withDayOfMonth(1);
        //季度结束时间
        LocalDate endOfQuarter = startOfQuarter.plusMonths(3).minusDays(1);

        //根据季度开始结束的月份,动态循环查询订单数据
        for (int month = startOfQuarter.getMonthValue(); month <= endOfQuarter.getMonthValue(); month++) {
            //所处季度中的每月开始时间
            LocalDate startOfMonth = startOfQuarter.withMonth(month).with(TemporalAdjusters.firstDayOfMonth());
            //所处季度中的每月结束时间
            LocalDate endOfMonth = startOfMonth.with(TemporalAdjusters.lastDayOfMonth());
            //查询一个月的团购订单数据
            List<GrouponOrder> grouponOrders = byDateTimeRangeGetGrouponOrderData(
                    storeNo,
                    LocalDateTime.of(startOfMonth, LocalTime.MIN),
                    LocalDateTime.of(endOfMonth, LocalTime.MAX)
            );
            //计算订单数
            Integer monthOrderCount = grouponOrders.size();
            //计算收益额
            BigDecimal monthRevenue = grouponOrders.stream()
                    .filter(grouponOrder -> getRevenueOrderStatus().contains(grouponOrder.getStatus()))
                    .map(grouponOrder -> grouponOrder.getTotalAmount())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            //计算退款额
            BigDecimal monthRefund = grouponOrders.stream()
                    .filter(grouponOrder -> getRefundOrderStatus().contains(grouponOrder.getStatus()))
                    .map(grouponOrder -> grouponOrder.getTotalAmount())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            quarterEveryMonthData.get("revenues").add(String.valueOf(monthRevenue.intValue()));
            quarterEveryMonthData.get("refunds").add(String.valueOf(monthRefund.intValue()));
            quarterEveryMonthData.get("orderCounts").add(String.valueOf(monthOrderCount));
        }

        return quarterEveryMonthData;
    }

    @Override
    public BaseResponse<String> updateByNo(Store store) {
        //查询旧数据
        Store byId = getById(store.getId());
        //判断地址有没有发生改变
        if (!byId.getAddress().equals(store.getAddress())) {
            AddressUtil.Location address = new AddressUtil.Location();
            //发生了改变
            try {
                address = AddressUtil.getAddress(store.getAddress());
            } catch (Exception e) {
                e.printStackTrace();
            }
            //修改经纬度
            store.setLatitude(address.getResult().getLocation().getLat());
            store.setLongitude(address.getResult().getLocation().getLng());
        }
        //修改数据
        updateById(store);
        //删除缓存
        redisTemplate.delete(STORE_LIST);
        //返回数据
        return ResultUtils.success("修改成功");
    }
}